module Explode.Player;
private import Explode.Stage;
private import Explode.Characters;
private import Explode.GameConfig;
private import Graphics.IGraphics;
private import Input.GamePad;

private import System.Console;

public class Player : BaseCharacter {
	protected const int IMGINDEX_DEAD   = 24;
	protected const int IMGINDEX_FALL   = 20;
	protected const int IMGINDEX_ROPE   = 16;
	protected const int IMGINDEX_LADDER = 12;
	protected const int IMGINDEX_WALK   = 8;
	protected const int IMGINDEX_STAND  = 4;
	protected const int IMGINDEX_DIGG        = 28;
	protected const int IMGINDEX_ROPE_DIGG   = 36;
	protected const int IMGINDEX_LADDER_DIGG = 32;

	protected static const int WALKDPEED = 2;

	protected bool _deadFlag , _fallFlag ,_ropeFlag , _ladderFlag , _goalFlag;
	protected ICharacter _digger;
	protected GameStage _stage;
	protected IGamePad _pad;
	protected IGraphics _graphics;
	protected string _textureName = "";
	protected string _cpu;

	protected int _lastxm , _lastym; // PCモード用

	public bool isDead(){
		return _deadFlag;
	} //
	public bool goalFlag() {
		return _goalFlag;
	} //
		
	public void cpu( string c){
		_cpu = c;
	} //
	
		
	public this( GameStage stg , IGraphics g ){
		Console.writeln("Create Player");
		_stage    = stg;
		_graphics = g;
		_attribute = Blocks.PLAYER;
		_pad = new VirtualGamePad();
		_sprite  = g.getSprite();
		reset();
	} //

	public void reset(){
		_digger		= null;
		_deadFlag	= false;
		_ropeFlag	= false;
		_ladderFlag	= false;
		_fallFlag	= false;
		_goalFlag	= false;
		_score		= 0;
		_x = 0 ; _y = 0; _z = 120;
		_lastxm		= 0;
		_lastym		= 0;

		if ( _textureName != _stage.playerTextureName ) {
			Console.writeln("Load Texture %s" , _stage.playerTextureName );
			_textureName = _stage.playerTextureName;
			_texture	 = _graphics.createTexture( "PLAYER", _textureName , 0 , 0 );
			_texture.setFrameSize( 32 , 48 );
		}
		_hFace = 1;
		_goldCnt = 0;
		_cpu	= GameConfig.cpu;
		_texture.frameIndex = 4;
		_sprite.visible =  true;
		_sprite.texture = _texture;
		_sprite.setSize( Blocks.WIDTH , Blocks.HEIGHT + Blocks.HALFHEIGHT );
		_sprite.moveTo( _x , _y );
		_sprite.z =  _z;
	} //

	public void padStatus( int st ){
		_pad.padStatus = st;
	} //

	public bool action() {
		if ( _deadFlag ) dyingAction();
		else	if( !(_digger is null) )	diggerAction();
		else		 						walkAction();
		checkDamepo();
		return true;
	} //
		
	protected bool dyingAction() {
		_xMove=0;
		_animCnt++;
		_texture.frameIndex = 24 + ( _animCnt / 15 % 4 );
		int bottom = ( _stage.rows ) * Blocks.HEIGHT;
		if ( _y > bottom - Blocks.HALFHEIGHT ) y = bottom - Blocks.HALFHEIGHT;
		return true;
	} //

	protected bool diggerAction(){

		if( !(cast(Digger) _digger).busy ){
			_digger = null;
			return walkAction();
		}

		_animCnt++;
		int imgIndex = 0;
		if( _deadFlag )
					imgIndex = IMGINDEX_DEAD ;
		else	if ( _fallFlag )
					imgIndex = IMGINDEX_FALL ;
		else	if ( _ropeFlag )
					imgIndex = IMGINDEX_ROPE_DIGG ;
		else	if ( _ladderFlag )
					imgIndex = IMGINDEX_LADDER_DIGG ;
		else		imgIndex = IMGINDEX_DIGG;

		 _texture.frameIndex = imgIndex + ( _animCnt / 4 % 4 );
		return true;
	} //

	protected bool walkAction() {
		alias Blocks B;
		 int xm = _pad.x , ym = _pad.y;
		 int xs = _x % B.WIDTH , ys = _y % B.HEIGHT;
		 int centerX = _x + B.HALFWIDTH  , centerY = _y + B.HALFHEIGHT ;
		 int bottomY = _y + B.HEIGHT - 1;
		 int cb = _stage.getXYPointBlock( centerX , centerY );
		 int fb = _stage.getXYPointBlock( centerX , _y + B.HEIGHT + ( ym - 1 ) );
		 bool kickFlag = false;

		if ( _cpu == "PC") { //走りっぱなし
			if ( xm == 0 ) xm = _lastxm ; 
			if ( ym == 0 ) ym = _lastym;
			if( _pad.x != 0 ) _lastym = ym = 0;
			if( _pad.y != 0 ) _lastxm = xm = 0;
			_lastxm = xm;
			_lastym = ym;
			if( _pad.getButton( 0 ) != 0 || _pad.getButton( 0 ) != 0 ) 
				xm = ym = _lastxm = _lastym = 0;
		}

		 _fallFlag = false;
		 _ropeFlag = false;
		if ( xm != 0 ) { //横移動判定
		  _hFace = ( xm > 0 ) ? 1 : -1;
			int xp = _x + (( xm > 0 ) ? B.WIDTH : -1);
			int  b = _stage.getXYPointBlock( xp , centerY );
			if ( xs == 0 && B.isMatch( b , B.MBLOCK ) ) kickFlag = true;

			if ( B.isWall(b) ) {
				xm = 0;// ブロックの境目にいる
			// 移動ショートカットのための補正
			 if ( ys == B.HALFWIDTH || ys == (B.HALFHEIGHT + WALKDPEED) ) { 
				int hb = _stage.getXYPointBlock( xp , _y - WALKDPEED * 2 );
				if ( !B.isMatch( hb , B.BLOCK ) && ym < 1 ) ym = -1;
			 }
			}
		}

		// 中心か足下のブロックがハシゴではなく、
		// かつ足下が敵ではない時は_ladderFlag = false
		if( !B.isLadder( cb|fb ) && !B.isEnemy( fb ) ) _ladderFlag = false;

		// 縦移動判定
		// 縦方向の入力がある時のみチェック
		if ( ym != 0 ) { //縦移動判定
			// 中心か足下のブロックがハシゴ_ladderFlag=true;
			if ( B.isLadder( cb | fb ) ) _ladderFlag = true;
			else // または、移動方向が下で足下が敵の時は_ladderFlag=true;
			if ( B.isEnemy( fb ) && ym > 0 ) _ladderFlag = true;


			// x座標が隣のブロックとの境目にいる、かつハシゴ上り状態ではない時の補正
			// かつ縦方向の入力がある
			if ( xs == B.HALFWIDTH && !_ladderFlag ) {
				// 中心＋右に一歩のブロック
				int  rb = _stage.getXYPointBlock( centerX + WALKDPEED , centerY );

				// 中心＋右に一歩のブロックで、移動方向が左以外の場合、
				// 下ではなく左に移動する。
				if ( B.isLadder( rb ) && xm > -1 ) {
					xm =  1;
					ym =  0;
				}
			}
			// ハシゴ以外で上方向に入力していた場合は無効にする
			if ( !_ladderFlag && ym < 0 ) ym = 0;

			//縦移動方向の座標
			int yp = _y + ( ( ym > 0 ) ? B.HEIGHT : -1);
			//縦移動方向にあるブロック
			int b = _stage.getXYPointBlock( centerX , yp );

			if ( ym >= 0 && B.isHHole( b ) )	b  = B.BLANK;
			if ( ym <  0 && B.isCeiling( b ) ) ym = 0;
			else
			if ( B.isWall(b) ) ym = 0;
			if ( ym != 0 ) xm=0;
		}
		// 落下判定
		// ハシゴ上り状態でない時のみチェックする。
		if ( !_ladderFlag ) { 
			// 足下のブロック
			int ffb = _stage.getXYPointBlock( centerX, _y + B.HEIGHT );

			// 中心がハシゴなら_ladderFlag = true
			if ( B.isLadder(cb) ) _ladderFlag = true;
			 else // 移動方向が下以外で、中心がロープ、高さがマス目ぴったりなら_ropeFlag = true
			if ( ym < 1 && B.isRope(cb) && ys == 0 ) {
				_ropeFlag = true;
				_y = this.by() * B.HEIGHT;
			} else
			if ( B.isFloor( ffb ) || B.isEnemy( ffb ) ) {
				// 足下が床か敵なら落下しない
				_fallFlag = false;
			} else {
				_fallFlag = true;
			}
		}

		// 穴掘る？
		if ( ( _pad.getButton(0) != 0 || _pad.getButton(1) != 0 ) && !_fallFlag ) {
			int mx = (centerX >> B.SHIFTX ) , by = this.by();
			int fx =  centerX + ((_pad.getButton(0) != 0 ) ? -B.WIDTH : +B.WIDTH );
			int fy =  centerY + B.HEIGHT;
			int ffb2 = _stage.getXYPointBlock( fx , fy );

			 if ( B.isBrick( ffb2 ) ) {
				int tb = _stage.getXYPointBlock( fx , centerY );
				if ( B.isBlank( tb ) ) {
					//( tb & ~( B.BLANK | B.HLADDER | B.DIGGER | B.PLAYER ) ) == 0 ) {
					_hFace = ( _pad.getButton( 1 ) == 0 ) ? -1 : 1;
					_digger = _stage.setDigger(
								this , fx >> B.SHIFTX,fy >> B.SHIFTY );
					moveTo( mx * B.WIDTH , this.by() * B.HEIGHT );
					xm = 0; ym = 0;
					_lastxm = _lastym = 0;
				}
			}
		}

		//ボンバー＆キック
		 if ( kickFlag ) {
			int hb = _stage.getXYBlock( bx + hFace , by );
			if ( !B.isMatch( hb , B.BLOCK ) ) {
			 _x = bx * B.WIDTH;
			 if ( !_fallFlag && !_ropeFlag && ym == 0  )
					_stage.kickBlock( this , bx + hFace , by , 1 );
			}
		 }


		if ( _fallFlag ) {
			_xMove = 0;
			_yMove = WALKSPEED;
		}else{
			_xMove = xm * WALKDPEED;
			_yMove = ym * WALKDPEED;
		}
		moveBy( xMove , yMove );

		// 位置補正
		if ( _xMove != 0 && y % B.HEIGHT != 0 ) {
			int xp2 = _x + (( _xMove > 0 ) ? B.WIDTH : -1 );
			int hp = _stage.getXYPointBlock( xp2 , _y );
			int fp = _stage.getXYPointBlock( xp2 , _y + B.HEIGHT - 1 );
			int cmp = B.BLOCK | B.HHOLE | B.LADDER | B.ROPE ;
			if( B.isMatch( hp | fp , cmp ) )
				_y += WALKDPEED * ( ( y % B.HEIGHT < B.HALFHEIGHT ) ? -1 : 1 );
		} else
		if ( _yMove != 0 && _x % B.WIDTH != 0 )
				_x += WALKDPEED * ( ( x % B.WIDTH  < B.HALFWIDTH  ) ? -1 : 1 );
		if( _x % B.WIDTH == 0 && _y % B.HEIGHT == 0 ) {
			if( B.isGold( cb ) ) getGold();
			if( B.isGoal(cb) )  _goalFlag = true;
		}



		bool  mv = ( _xMove != 0 || _yMove != 0);

		if( _xMove != 0 && _yMove == 0 ) { _ladderFlag = false; _fallFlag=false; }

		if( mv ) _animCnt++;
		int imgIndex = 0;

		if( _deadFlag )
					imgIndex = IMGINDEX_DEAD ;
		else	if ( _fallFlag )
					imgIndex = IMGINDEX_FALL ;
		else	if ( _ropeFlag )
					imgIndex = IMGINDEX_ROPE ;
		else	if ( _ladderFlag )
					imgIndex = IMGINDEX_LADDER;
		else	if ( mv )
					imgIndex = IMGINDEX_WALK;
		else	{	imgIndex = IMGINDEX_STAND ; _animCnt++; }
		 _texture.frameIndex = imgIndex + ( _animCnt / 4 % 4 );
		return true;
	} //

	public bool drawingEntry( IGraphics g ) {
		_sprite.flipH = ( _hFace < 0 ) ? true : false;
		_sprite.moveTo( _x , _y -Blocks.HALFHEIGHT );
		_sprite.drawingEntry( g );
		return true;
	} //

	public bool draw( IGraphics g ) {
		return true;
	} //


	public int getGold() {
		_goldCnt += _stage.getGold( bx , by , true );
		_score += GameConfig.SCORE_GOLD;
		return 1;
	} //



	public void  moveTo( int xx , int yy ) {
		_x = xx; _y = yy;
	} //
	public void moveBy( int xx , int yy) {
		moveTo( _x + xx , _y + yy );
	} //


	public bool onCollided( ICharacter obj) {
		switch( obj.attribute ) {
			case Blocks.DIGGER:
			case Blocks.ENEMY:
				die();
				break;
			case Blocks.MBLOCK:
				if ( obj.y < y ) die();
				break;
			default:
				break;
		}
		return false;
	}

	protected void die() {
		_deadFlag = true;
		_yMove = -8;
//			_texture.frameIndex = PlayerImages.DIE + (_animCnt % 4 );
	}

	public void giveup() {
		die();
	} //

	public bool kill() {
		return true;
	} //
		
	private void checkDamepo() {
		int bx = this.bx , by = this.by;
		int cb = _stage.getXYBlock(bx     , by    );
		int lb = _stage.getXYBlock(bx - 1 , by    );
		int rb = _stage.getXYBlock(bx + 1 , by    );
		int db = _stage.getXYBlock(bx     , by + 1);
		if ( Blocks.isHHole( cb ) ) { //落とし穴通過マークをつける
			_stage.addXYBlockMask( bx , by , Blocks.PLMASK );
		}
		if ( _ladderFlag || _fallFlag ) return;
		if (	!Blocks.isLadder(cb) 
				&& !_fallFlag
				&& Blocks.isWall(lb)  
				&& Blocks.isWall(rb)  
				&& Blocks.isWall(db) 
		) {
//			_stage.zoomIn();
		}
	} //
} //


/**
*	掘った穴。穴はオブジェクトである。
**/
public class Digger : BaseCharacter{
	protected static int _IDCNT = 0;

	protected ISprite _effect;
	protected ITexture _etexture;

	protected BaseCharacter _master;
	protected GameStage _stage;
	protected bool _busy;
	public bool busy(){return _busy;}

	protected int _timeCntStart  = 340;
	protected int _timeCnt       = 340;
	protected int _busyTime      = 14;
	protected int _spriteHeight;
	protected int id             = 0;
	protected bool _alive = false;
	protected IGraphics _graphics;

	public this( GameStage stg , IGraphics g , ITexture tex = null ){
		id = _IDCNT++;
		_stage = stg;
		_attribute = Blocks.DIGGER;
		_graphics = g;
		_z = 110;
		if( !(tex is null ) ) _texture  = _texture;
		initImage( g );
		reset();
	} //

	private void  initImage( IGraphics g ) {
	} //

	public void call( int x, int y , GameStage stg , ICharacter mst ) {
		reset();
		_master = cast(BaseCharacter)mst;
		_busy   = true;
		_alive  = true;
		_stage = stg;
		BaseCharacter m = cast(BaseCharacter)_master;
		_etexture = m.texture;
		moveTo( x * Blocks.WIDTH , y * Blocks.HEIGHT );
		_effect.visible = true;
	} //
	
	public bool alive(){return _alive;}
	
	
	public void reset() {
		if ( _sprite is null ) {
			_sprite = _graphics.getSprite();
			_effect = _graphics.getSprite();
		}
		_sprite.setSize( Blocks.WIDTH , Blocks.HEIGHT );
		_effect.setSize( Blocks.WIDTH , Blocks.HEIGHT );
		_spriteHeight = Blocks.HEIGHT;
		_alive   = false;
		_busy    = false;
		_timeCnt = _timeCntStart;
	} //
	

	public bool action() {
		int bx = this.bx() , by = this.by();
		  if ( _timeCnt > _timeCntStart - _busyTime ) {
			if ( _spriteHeight > 0 ) _spriteHeight -= 1;
			 int ob = _stage.getXYBlock( bx , by - 1 );
				_stage.setDiggerMask( bx , by );
			 if ( Blocks.isEnemy( ob ) ) {
				 kill();
			}
		  } else
		  if ( _timeCnt > 64 ) {
			_spriteHeight = 0;
			if ( _timeCnt == _timeCntStart - _busyTime ) {
				_stage.setXYBlock( bx , by , Blocks.BLANK);
				_stage.setDiggerMask( bx , by);
				_effect.visible = false;
				_busy = false;
			}
		  } else {
			if ( _spriteHeight < Blocks.HEIGHT && _timeCnt % 4 == 0 ) _spriteHeight++;
		  }
			if( _timeCnt == 0 )kill();
		_timeCnt--;
		return true;
	} //

	public bool drawingEntry( IGraphics g ) {
		return g.addDrawingEntry( this );
	} //
	/**
	*	描画。Diggerは自分のテクスチャを持たず、ステージとプレイヤーから借りる。
	*/
	public bool draw( IGraphics g ) {
		if ( _spriteHeight >= 4 ) {
			ITexture tex = _stage.texture;
			int ix = tex.frameIndex;
			tex.frameIndex = 4;
			{
				_sprite.texture = tex;
				_sprite.height = _spriteHeight;
				_sprite.visible = true;
				_sprite.moveTo( x  , y + (Blocks.HEIGHT - _spriteHeight) );
				_sprite.draw( g );
			}
			tex.frameIndex = ix;
		}

		if ( _effect.visible ) {
			ITexture tex = _master.texture;
			int ix = tex.frameIndex;
			int bm = g.blendMode;
			g.blendMode = g.ALPHA_ADD;
			{
				tex.frameIndex = 44 + (_timeCnt % 4 );
				_effect.texture = tex;
				_effect.opacity = .5;
				_effect.moveTo( x , y - Blocks.HALFHEIGHT + (Blocks.HEIGHT - _spriteHeight) );
				_effect.draw( g );
			}
			g.blendMode = bm;
			tex.frameIndex = ix;
		}
		return true;
	}
	public int getGold() {
		return 0;
	} //



	public void  moveTo( int xx , int yy ) {
		_x = xx; _y = yy;
	} //
	public void moveBy( int xx , int yy) {
		moveTo( _x + xx , _y + yy );
	} //

	public bool collision( ICharacter obj ) {
		if ( _timeCnt > 0 ) return false;
		else return super.collision( obj );
	} //


	public bool  onCollided( ICharacter obj ) {
		bool ret = false;
		switch ( obj.attribute ) {
			case Blocks.ENEMY:
			case Blocks.PLAYER:
				 ret = true;
				break;
			default:
				break;
		}
		return ret;
	} //

	public bool kill() {
		if ( _alive ) {
			_stage.resetDiggerMask( bx , by );
			_stage.setXYBlock( bx , by , Blocks.BRICK );
		}
		_timeCnt = 1;
		_alive = false;
		_busy  = false;
		return true;
	}

	invariant {
		assert( _x >= 0 );
		assert( _y >= 0 );
	}

 } //



